package com.icloud.cer.rest.msg.codec;

import java.io.IOException;
import java.nio.BufferUnderflowException;

import org.apache.mina.core.buffer.BufferDataException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.icloud.cer.rest.msg.MessageHeader;
import com.icloud.cer.rest.msg.MessageHolder;
import com.icloud.cer.rest.msg.MsgConstants;
import com.icloud.cer.rest.msg.bean.ClientEncyptRequest;
import com.icloud.cer.rest.msg.bean.EncryptResponse;
import com.icloud.cer.rest.msg.bean.HeartbeatResponse;
import com.icloud.cer.rest.msg.bean.RegistInfo;
import com.icloud.cer.rest.util.LogUtils;

public class ObjectDecoder extends CumulativeProtocolDecoder{
	private static final Logger log = LoggerFactory.getLogger(ObjectDecoder.class);
	// 消息类型长度，是个整形
	private int prefixLength = MessageHeader.BYTE_COUNT;
	@Override
	protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		int oldPos = in.position();
		log.debug("dodecode start");
		MessageHeader pheader = prefixedDataAvailable(in);
		if (pheader != null && in.remaining() >= pheader.getSize_()) {
			MessageHolder body = null;
			try {
				body = getMessageBodyHolder(session, in, pheader);
				if (body == null) {
					log.error("Mina decode to object is NULL.");
					return true;
				}
			} catch (Exception e) {
				log.error("The message parse to MessageBodyHolder course exception.", e);
				return true;
			}
			out.write(body);
			return true;
		} else {
			// 还原成之前的位置
			in.position(oldPos);
			log.error("dodecode error, data:" + in);
			return false;
		}
	}
	
	/**
	 *  判断是否有足够长度的字节
	 * @param in
	 * @return
	 */
	protected MessageHeader prefixedDataAvailable(IoBuffer in) {
//		if (in.remaining() < prefixLength) {
//			log.info("remaining < prefixLength");
//			return null;
//		}
		// 得到包头
		byte[] headerByte = new byte[prefixLength];
		in.get(headerByte, 0, prefixLength);
		// 包头结构
		MessageHeader pheader = new MessageHeader();
		try {
			pheader.deserialize(headerByte);
		} catch (IOException e) {
			log.error("", e);
			throw new RuntimeException(e.getMessage(),e);
		}

		int dataLength = pheader.getSize_();
		if (dataLength < 0) {
			throw new BufferDataException("dataLength: " + dataLength);
		}
		return pheader;

	}

	public MessageHolder getMessageBodyHolder(IoSession session, IoBuffer in, MessageHeader header) throws IOException {
		short command = header.getType_();
		char flag = header.getFlag_();
		LogUtils.printMessage(log, command, session);

		switch (command) {
		case MsgConstants.CLIENT_REQ_REG:
			return getResHolder(command, flag, in, header);
		case MsgConstants.CLIENT_RES_HEARTBEAT:
			return getHeartbeatHolder(command, flag, in, header);
		case MsgConstants.CLIENT_REQ_ENCRYPT:
			return getEncryptTask(command, flag, in, header);
		case MsgConstants.CLIENT_RES_ENCRYPT:
			return getEncryptResponseHolder(command, flag, in, header);
		default:
			log.error("error command:" + command);
			break;
		}
		return null;
	}
	
	public MessageHolder getResHolder(short command, char flag, IoBuffer in,MessageHeader header) {
		byte[] data = new byte[RegistInfo.BYTE_COUNT];
		int oldLimit = in.limit();
		int end = RegistInfo.BYTE_COUNT;
		try {
			if(end > oldLimit) {
				throw new BufferUnderflowException();
			}
			in.get(data, 0, RegistInfo.BYTE_COUNT);
			MessageHolder bodyHolder = new MessageHolder(command);
			bodyHolder.setHead(header);
			RegistInfo ri = getRegistInfo(flag, data);
			//bodyHolder.setSequence(ri.getSequence());
			bodyHolder.setValue(ri);
			return bodyHolder;
		} finally {
			in.limit(oldLimit);
		}
	}
	
	
	public MessageHolder getHeartbeatHolder(short command, char flag, IoBuffer in,MessageHeader header) {
		byte[] data = new byte[HeartbeatResponse.BYTE_COUNT];
		int oldLimit = in.limit();
		int end = HeartbeatResponse.BYTE_COUNT;
		try {
			if(end > oldLimit) {
				throw new BufferUnderflowException();
			}
			in.get(data, 0, HeartbeatResponse.BYTE_COUNT);
			MessageHolder bodyHolder = new MessageHolder(command);
			bodyHolder.setHead(header);
			HeartbeatResponse ri = getHeartbeatResponse(flag, data);
			bodyHolder.setSequence(ri.getSequence());
			bodyHolder.setValue(ri);
			return bodyHolder;
		} finally {
			in.limit(oldLimit);
		}
	}
	
	public RegistInfo getRegistInfo(char flag, byte[] data) {
		RegistInfo regInfo = new RegistInfo();
		try {
			regInfo.deserialize(data,flag);
		} catch (IOException e) {
			log.error("deserialize RegistInfo failed." + e.getMessage());
		}
		
		return regInfo;
	}
	
	public HeartbeatResponse getHeartbeatResponse(char flag, byte[] data) {
		HeartbeatResponse heartBeat = new HeartbeatResponse();
		try {
			heartBeat.deserialize(data,flag);
		} catch (IOException e) {
			log.error("deserialize RegistInfo failed." + e.getMessage());
		}
		return heartBeat;
	}
	
	public MessageHolder getEncryptTask(short command, char flag, IoBuffer in,MessageHeader header) {
		byte[] data = new byte[ClientEncyptRequest.BYTE_COUNT];
		int oldLimit = in.limit();
		int end = ClientEncyptRequest.BYTE_COUNT;
		try {
			if(end > oldLimit) {
				throw new BufferUnderflowException();
			}
			in.get(data, 0, ClientEncyptRequest.BYTE_COUNT);
			MessageHolder bodyHolder = new MessageHolder(command);
			bodyHolder.setHead(header);
			ClientEncyptRequest ri = buildEncryptRequest(flag, data);
//			bodyHolder.setSequence(ri.getSequence());
			bodyHolder.setValue(ri);
			return bodyHolder;
		} finally {
			in.limit(oldLimit);
		}
	}
	
	public MessageHolder getEncryptResponseHolder(short command, char flag, IoBuffer in,MessageHeader header) {
		byte[] data = new byte[EncryptResponse.BYTE_COUNT];
		int oldLimit = in.limit();
		int end = EncryptResponse.BYTE_COUNT;
		try {
			if(end > oldLimit) {
				throw new BufferUnderflowException();
			}
			in.get(data, 0, EncryptResponse.BYTE_COUNT);
			MessageHolder bodyHolder = new MessageHolder(command);
			bodyHolder.setHead(header);
			EncryptResponse ri = getEncryptResponse(flag, data);
			bodyHolder.setSequence(ri.getSequence());
			bodyHolder.setValue(ri);
			return bodyHolder;
		} finally {
			in.limit(oldLimit);
		}
	}
	
	public ClientEncyptRequest buildEncryptRequest(char flag, byte[] data){
		ClientEncyptRequest si = new ClientEncyptRequest();
		try {
			si.deserialize(data,flag);
		} catch (IOException e) {
			log.error("deserialize message body failed." + e.getMessage());
		}
		return si;
	}
	
	public EncryptResponse getEncryptResponse(char flag, byte[] data){
		EncryptResponse er = new EncryptResponse();
		try {
			er.deserialize(data,flag);
		} catch (IOException e) {
			log.error("deserialize EncryptResponse body failed." + e.getMessage());
		}
		return er;
	}
}
